package com.ddwl.user.service;

import com.ddwl.common.constant.*;
import com.ddwl.common.exception.CommonException;
import com.ddwl.common.exception.ExceptionCode;
import com.ddwl.common.feign.api.GateApi;
import com.ddwl.common.feign.api.MessageApi;
import com.ddwl.common.feign.api.WalletApi;
import com.ddwl.common.service.RedisService;
import com.ddwl.common.util.BeanConverUtils;
import com.ddwl.common.util.ObjectUtil;
import com.ddwl.schema.bo.message.TempSentSmsBo;
import com.ddwl.schema.bo.user.DeviceBo;
import com.ddwl.schema.bo.user.DeviceSearchBo;
import com.ddwl.schema.bo.user.DeviceShareBo;
import com.ddwl.schema.util.DateUtil;
import com.ddwl.schema.vo.user.DeviceVo;
import com.ddwl.schema.vo.user.UserVo;
import com.ddwl.user.dao.model.Device;
import com.ddwl.user.dao.model.DeviceConnRecord;
import com.ddwl.user.repository.DeviceConnRecordRepository;
import com.ddwl.user.repository.DeviceRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Fu Zhaohui
 * @date 2017/2/26
 */
@Service
@RequiredArgsConstructor
public class DeviceService {

    private final DeviceRepository deviceRepository;
    private final DeviceConnRecordRepository deviceConnRecordRepository;
    private final UserService userService;
    private final MessageApi messageApi;
    private final GateApi gateApi;
    private final WalletApi walletApi;
    private final RedisService redisService;

    public void save(DeviceBo bo) {
        Device device = deviceRepository.findByUserIdAndMac(bo.getUserId(), bo.getMac());
        if (device == null) {
            if (deviceRepository.countByMac(bo.getMac()) > 0) {
                throw new CommonException("宝贝已被绑定使用，请联系主人共享");
            }
            device = new Device();
        }
        BeanUtils.copyProperties(bo, device, ObjectUtil.getNullFields(bo));
        device.setGmtCreated(Instant.now());
        deviceRepository.save(device);
    }

    public void updateSelected(DeviceBo bo) {
        Device entity = deviceRepository.findByUserIdAndMac(bo.getUserId(), bo.getMac());
        if (entity == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        entity.setSelected(1);
        deviceRepository.save(entity);
    }

    /**
     * 删除设备
     *
     * @param userId
     * @param mac
     */
    public void delete(String userId, String mac) {
        deviceRepository.deleteByUserIdAndMac(userId, mac);
    }

    /**
     * 我的设备
     *
     * @param userId
     * @return
     */
    public List<DeviceVo> user(String userId, String platform) {
        Device device = new Device();
        device.setUserId(userId);
        device.setPlatform(platform);
        Example<Device> example = Example.of(device, ExampleMatcher.matching());
        List<Device> entities = deviceRepository.findAll(example, Sort.by(Sort.Direction.DESC, "gmtCreated"));
        return entities.stream().map(entity -> BeanConverUtils.convertBean(entity, DeviceVo.class)).collect(Collectors.toList());
    }

    /**
     * 查询用户所有设备Mac地址
     *
     * @param userId
     * @return
     */
    public List<String> getMacs(String userId, String platform) {
        return deviceRepository.findByUserIdAndPlatform(userId, platform).stream().map(Device::getMac).distinct().collect(Collectors.toList());
    }

    public List<DeviceVo> near(DeviceSearchBo param) {
        return deviceRepository.near(param.getLoc(), param.getPlatform()).stream().map(device -> {
            DeviceVo vo = new DeviceVo();
            BeanUtils.copyProperties(device, vo);
            vo.setSelf(param.getUserId().equals(device.getUserId()));
            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 分享设备
     *
     * @param bo
     */
    public void share(DeviceShareBo bo) {
        Device device = deviceRepository.findByUserIdAndMac(bo.getUserId(), bo.getMac());
        if (device == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        if (deviceRepository.countByMac(bo.getMac()) >= 4) {
            throw new CommonException("宝贝已超过最大分享人数");
        }
        UserVo userVo = userService.getByPhone(bo.getMobile());
        if (userVo == null) {
            TempSentSmsBo tempSentSmsBo = new TempSentSmsBo();
            tempSentSmsBo.setToPhone(bo.getMobile());
            tempSentSmsBo.setTemplateCode(SmsTempCodeEnum.DEVICE_SHARE.getCode());
            messageApi.sentSms(tempSentSmsBo);
            return;
        }
        device.setId(null);
        device.setUserId(userVo.getId());
        deviceRepository.save(device);
    }

    public void conn(DeviceBo bo) {
        DeviceConnRecord dcr = new DeviceConnRecord();
        BeanUtils.copyProperties(bo, dcr, ObjectUtil.getNullFields(bo));
        dcr.setGmtCreated(Instant.now());
        Integer continuousDay = 1;
        dcr.setContinuousDay(continuousDay);
        DeviceConnRecord last = deviceConnRecordRepository.getLast(bo.getUserId());
        if (null != last) {
            if (last.getGmtCreated().compareTo(DateUtil.getTimesMorning()) >= 0) {
                continuousDay = Optional.ofNullable(last.getContinuousDay()).orElse(1);
            } else if (last.getGmtCreated().compareTo(DateUtil.getTimesMorning(-1)) >= 0) {
                continuousDay = Optional.ofNullable(last.getContinuousDay()).orElse(1) + 1;
            }
        }
        DeviceConnRecord save = deviceConnRecordRepository.save(dcr);
        Boolean flag = judgeWhetherReward(save.getId(), save.getUserId(), continuousDay);
        if (flag) {
            save.setContinuousDay(0);
        } else {
            save.setContinuousDay(continuousDay);
        }
        deviceConnRecordRepository.updateContinuousDay(save);
    }

    private Boolean judgeWhetherReward(String recordId, String userId, Integer continuousDay) {
        String days = gateApi.getSettingVal(GateEnum.DictionaryTag.TAG_SYSTEM.name(), GateEnum.DictionaryField.DEVICE_CONN_DAYS.name()).getResult();
        if (continuousDay < Integer.valueOf(days)) {
            return Boolean.FALSE;
        }
        String reward = gateApi.getSettingVal(GateEnum.DictionaryTag.TAG_SYSTEM.name(), GateEnum.DictionaryField.CONN_REWARD.name()).getResult();
        walletApi.changeWallet(recordId, userId, DistConstant.WalletFlowType.DEVICE_CONN_REWARD.getType(), new BigDecimal(reward));
        return Boolean.TRUE;
    }

    /**
     * 连接设备
     *
     * @param bo
     */
    public void connected(DeviceBo bo) {
        String mac = bo.getMac().replaceAll(":", "");
        HashOperations<String, String, String> mapOps = redisService.getHashOps();
        String key = RedisKey.ONLINE_DEVICE_KEY.getKey(mac);
        Map<String, String> entryMap = mapOps.entries(key);
        String timestamp = DateUtil.getCurrDate(DateUtil.DATE_TIME_FORMAT);
        if (!CollectionUtils.isEmpty(entryMap)) {
            if (!entryMap.get("userId").equals(bo.getUserId())) {
                throw new CommonException("设备当前已被连接");
            }
            mapOps.getOperations().expire(key, 5, TimeUnit.SECONDS);
            return;
        }
        Device device = deviceRepository.findByUserIdAndMac(bo.getUserId(), bo.getMac());
        if (device == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        device.setConnected(UserEnum.ConnectEnum.CONNECTED.getCode());
        device.setGmtOnline(Instant.now());
        deviceRepository.save(device);
        mapOps.put(key, "userId", bo.getUserId());
        mapOps.put(key, "timestamp", timestamp);
        mapOps.getOperations().expire(key, 1, TimeUnit.HOURS);

    }

    /**
     * 断开连接
     *
     * @param bo
     */
    public void disconnect(DeviceBo bo) {
        String mac = bo.getMac().replaceAll(":", "");
        redisService.getHashOps().getOperations().delete(RedisKey.ONLINE_DEVICE_KEY.getKey(mac));
        Device device = deviceRepository.findByUserIdAndMac(bo.getUserId(), bo.getMac());
        if (device == null) {
            return;
        }
        device.setConnected(UserEnum.ConnectEnum.DIS_CONNECT.getCode());
        device.setGmtOnline(Instant.now());
        deviceRepository.save(device);
    }

    /**
     * 当前连接中设备
     *
     * @return
     */
    public List<DeviceVo> online() {
        List<Device> devices = deviceRepository.findByConnected(UserEnum.ConnectEnum.CONNECTED.getCode());
        return BeanConverUtils.convertBeanList(devices, DeviceVo.class);
    }

    /**
     * 设备信息
     *
     * @param bo
     * @return
     */
    public DeviceVo info(DeviceBo bo) {
        return BeanConverUtils.convertBean(deviceRepository.findByUserIdAndMac(bo.getUserId(), bo.getMac()), DeviceVo.class);
    }
}
